package gates;

import exceptions.noQubitException;
import register.Register;
import mathtools.*;
import gates.MatrixGate;

/**
 * <p>Not Gate in Matrix representation for a specified Target bit or the entire
 * Quantum Register using target = -1 flag.<br /></p>
 * 
 * <p>The Gate is constructed by applications of the tensor product between
 * the 2x2 representation of Not and the 2x2 Identity Matrix. The number of
 * applications of the tensor product is proportional to the size of the 
 * Quantum Register. For the specified target, I2 was applied to the left
 * of Not magnitude(target) applications. I2 was applied to the right of Not
 * (numQubits - (target+1)) applications.</p>
 * 
 * <p>For example a 4 Qubit Quantum Register with target 2 of Qubits(0,1,2 and 3)
 * would be:<br\>
 * notGate = I x I x Not x I<br />
 * Where x represents the Tensor Product, notGate is the construction matrix,
 *  I is the 2x2 Identity matrix and Not is a 2x2 inversion matrix (0->1 and 1->0).</p>
 * @author Richard Inskip
 *
 */
public class NotMatrix extends MatrixGate {

	/* I2 - Identity matrix 2x2
	 * Not - 2x2 Not matrix :	|0 1|
	 * 							|1 0|
	 * target - the specific bit to "Not around" or flag to Not entire Register.
	 * notGate is the construction variable.
	 */
	
	/** Store a 2x2 identity matrix */
	private Matrix I2 = Matrix.IdentityMatrix(2);
	/** store a 2x2 not matrix */
	private Matrix Not;
	/** Store the target qubit the gate acts on*/	
	private int target;
	/** Matrix needed in the construction of the gate */
	private Matrix notGate;

	/**
	 * Constructor. If target = -1 this will flag to Not the entire Register.
	 * @param target The target big to perform the Not gate around. 
	 */
	protected NotMatrix(int target){

		// Set up variables and constant Matrices
		this.target = target;
		Complex num1 = new Complex(1.0,0.0);
		Not = new Matrix(2,2,0.0);
		Not.setElement(0,1,num1);
		Not.setElement(1,0,num1);

		/* Begin construction of notGate depending on the value of target.
		 * If target = 0 or -1 Not will be required on the far left of 
		 * the tensor product, therefore we set the initial variable
		 * of notGate as Not, however, if this is not the case the first
		 * variable will be I2, the 2x2 identity matrix.
		 */
		if(target==0 || target==-1){
			notGate = Not;
		}
		else{
			notGate = I2;
		}
	}

	/** Apply Method to apply Not Gate (Matrix) to a Quantum Register.
	 * @param r The Quantum Register
	 * @exception noQubitException If the target is outside the size of the Quantum Register throw Exception.
	 */
	public void apply(Register r) {
		// Find the length of the Quantum Register
		int numQubits = r.getNumQbits(); 

		/* Check to make sure the register is of adequate size for 
		 * the specified target. If not throw exception.
		 */
		if(target>(numQubits-1) || target<-1){
			throw new noQubitException("ERROR: Target specified for Not Gate (Matrix) is not within Quantum Register.");
		}


		/* If target is -1 (flag) apply Not Gate to the entire matrix.
		 * If target is not -1 than carry on with the remaining construction
		 * of notGate.
		 */
		if(target==-1){
			for(int i=1; i<numQubits;i++){
				notGate = notGate.tensorProduct(notGate);
			}
		}
		else{
			for(int i=1; i<numQubits;i++){
				if(i==target){
					notGate = notGate.tensorProduct(Not);
				}
				else{
					notGate = notGate.tensorProduct(I2);
				}
			}
		}
		// Apply notGate to the register and then update the register.
		r.update(notGate.multiplyMatrix(r));
	}

	/**
	 * Return the name of the Gate.
	 */
	public String getName() {
		return "Not";
	}

}
